package com.qsd.doc4jsimpple;

/**
 * * @Author xujunxia
 * * @Date 2024-12-06 14:59
 * * @Description TODO
 * * wb-0455@dtdream.com
 **/

import com.qsd.doc4jsimpple.constants.MultiLevelTypeEnum;
import com.qsd.doc4jsimpple.item.DocLvl;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.wml.*;
import org.docx4j.wml.Numbering.Num;

import java.io.File;
import java.math.BigInteger;

public class MultipleNumberingDefinitionsExample {

    public static void main(String[] args) throws Exception {
        try {
            // 创建一个新的Word文档对象
            WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();

            Numbering numbering1 = new Numbering();
            appenLvl(BigInteger.ONE, numbering1, DocLvl.getDefaultLvl().setValueTemplate("%1."));
            // 创建第一个NumberingDefinitionsPart（阿拉伯数字编号）
            NumberingDefinitionsPart numberingPart1 = createArabicNumberingDefinitionsPart(numbering1);
            numberingPart1.setJaxbElement(numbering1);
            wordPackage.getMainDocumentPart().addTargetPart(numberingPart1);


            // 创建段落并应用第一个编号定义
            P paragraph1 = createParagraphWithNumbering("这是第一个编号段落1", 1, BigInteger.ONE);
            wordPackage.getMainDocumentPart().addObject(paragraph1);
            P paragraph2 = createParagraphWithNumbering("这是第一个编号段落2", 1, BigInteger.ONE);
            wordPackage.getMainDocumentPart().addObject(paragraph2);


            // 创建第二个NumberingDefinitionsPart（字母编号）
            Numbering numbering2 = new Numbering();
            appenLvl(BigInteger.valueOf(2), numbering2, DocLvl.getDefaultLvl().setValueTemplate("\u2022"));
            NumberingDefinitionsPart numberingPart2 = createLetterNumberingDefinitionsPart();
            wordPackage.getMainDocumentPart().addTargetPart(numberingPart2);
            numberingPart2.setJaxbElement(numbering2);
            // 创建段落并应用第二个编号定义
            P paragraph3 = createParagraphWithNumbering("这是第二个编号段落1", 2, BigInteger.valueOf(2));
            wordPackage.getMainDocumentPart().addObject(paragraph3);
            P paragraph4 = createParagraphWithNumbering("这是第二个编号段落2", 2, BigInteger.valueOf(2));
            wordPackage.getMainDocumentPart().addObject(paragraph4);

            // 保存文档
            wordPackage.save(new File("multiple_numbering_example.docx"));
            System.out.println("文档已成功创建！");
        } catch (Docx4JException e) {
            e.printStackTrace();
        }
    }

    // 创建阿拉伯数字编号的NumberingDefinitionsPart
    private static NumberingDefinitionsPart createArabicNumberingDefinitionsPart(Numbering numbering) throws Exception {
        NumberingDefinitionsPart numberingPart = new NumberingDefinitionsPart();
        //数字编号定义 number 1
        BigInteger abstractNumId = BigInteger.valueOf(1);
        // 创建AbstractNum（抽象编号定义），设置为阿拉伯数字编号样式
        Numbering.AbstractNum abstractNum = new Numbering.AbstractNum();
        abstractNum.setAbstractNumId(abstractNumId);

        // 设置编号级别，这里简单设置一级编号
        Lvl lvl = new Lvl();
        lvl.setIlvl(BigInteger.valueOf(0));
        NumFmt numFmt = new NumFmt();
        numFmt.setVal(NumberFormat.DECIMAL);
        lvl.setNumFmt(numFmt); // 阿拉伯数字格式

        Lvl.LvlText lvltext = new Lvl.LvlText();
        //表示显示第一级编号，后面跟一个“.”
        //  lvltext.setVal("\u2022");
        lvltext.setVal("%1.");// 编号文本格式
        lvl.setLvlText(lvltext);
        abstractNum.getLvl().add(lvl);

        // 创建Numbering并添加AbstractNum

        Num num = new Num();
        num.setNumId(BigInteger.valueOf(1));
        Numbering.Num.AbstractNumId anid = new Numbering.Num.AbstractNumId();
        anid.setVal(abstractNumId);
        num.setAbstractNumId(anid);
        numbering.getNum().add(num);

        try {
            numberingPart.setJaxbElement(numbering);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return numberingPart;
    }

    // 创建字母编号的NumberingDefinitionsPart
    private static NumberingDefinitionsPart createLetterNumberingDefinitionsPart() throws Exception {
        NumberingDefinitionsPart numberingPart = new NumberingDefinitionsPart();
        //数字编号定义 number 2
        BigInteger abstractNumId = BigInteger.valueOf(2);
        // 创建AbstractNum（抽象编号定义），设置为字母编号样式
        Numbering.AbstractNum abstractNum = new Numbering.AbstractNum();
        abstractNum.setAbstractNumId(abstractNumId);

        // 设置编号级别，这里简单设置一级编号
        Lvl lvl = new Lvl();
        lvl.setIlvl(BigInteger.valueOf(0));
        NumFmt numFmt = new NumFmt();
        numFmt.setVal(NumberFormat.LOWER_LETTER);
        lvl.setNumFmt(numFmt); // 阿拉伯数字格式

        Lvl.LvlText lvltext = new Lvl.LvlText();
        //表示显示第一级编号，后面跟一个“.”
        //  lvltext.setVal("\u2022");
        lvltext.setVal("%1.");// 编号文本格式
        lvl.setLvlText(lvltext);
        abstractNum.getLvl().add(lvl);

        // 创建Numbering并添加AbstractNum
        Numbering numbering = new Numbering();
        Num num = new Num();
        num.setNumId(abstractNumId);
        Numbering.Num.AbstractNumId anid = new Numbering.Num.AbstractNumId();
        anid.setVal(abstractNumId);
        num.setAbstractNumId(anid);
        numbering.getNum().add(num);

        try {
            numberingPart.setJaxbElement(numbering);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return numberingPart;
    }

    // 创建带有指定编号定义的段落
    private static P createParagraphWithNumbering(String textContent, int numId, BigInteger abstractNumId) throws Exception {
        P paragraph = Context.getWmlObjectFactory().createP();

        // 创建一个运行（包含文本的最小单元）
        R run = Context.getWmlObjectFactory().createR();
        Text text = Context.getWmlObjectFactory().createText();
        text.setValue(textContent);
        run.getContent().add(text);

        // 将运行添加到段落
        paragraph.getContent().add(run);

        // 设置段落的编号属性，应用指定的编号定义
        org.docx4j.wml.PPr pPr = Context.getWmlObjectFactory().createPPr();
        org.docx4j.wml.PPrBase.NumPr numPr = Context.getWmlObjectFactory().createPPrBaseNumPr();
        PPrBase.NumPr.NumId numIdElement = new PPrBase.NumPr.NumId();
        numIdElement.setVal(BigInteger.valueOf(numId));
        numPr.setNumId(numIdElement);

        PPrBase.NumPr.NumId abstractNUmId = new PPrBase.NumPr.NumId();
        abstractNUmId.setVal(abstractNumId);

        // 关联到对应的NumberingDefinitionsPart
        numPr.setNumId(abstractNUmId);
        pPr.setNumPr(numPr);
        paragraph.setPPr(pPr);

        return paragraph;
    }


    public static void appenLvl(BigInteger abstractNumId, Numbering numbering, DocLvl docLvl) {
        //首先定义AbstractNum部分
        Numbering.AbstractNum an = new Numbering.AbstractNum();

        an.setAbstractNumId(abstractNumId);
        an.setMultiLevelType(MultiLevelTypeEnum.SINGLE_LEVEL);

        //定义0级，即最高一级的编号
        //只定义这一个级别
        Lvl lvl = new Lvl();
        lvl.setIlvl(docLvl.getLevel());

        //设置开始序号，如果不设置为1的话默认为0，可能出现某些格式的序号没有0值显示
        Lvl.Start start = new Lvl.Start();
        start.setVal(docLvl.getStart());
        lvl.setStart(start);

        //设置显示文本内容，如果不设置的话将不显示编号
        Lvl.LvlText lvltext = new Lvl.LvlText();
        //表示显示第一级编号，后面跟一个“.”
        //  lvltext.setVal("\u2022");
        lvltext.setVal(docLvl.getValueTemplate());
        lvl.setLvlText(lvltext);


        RPr rPr = new RPr();
        HpsMeasure sz = new HpsMeasure();
        sz.setVal(docLvl.getDocLvlFormat().getContentPound()); // 设置圆点大小为36磅
        rPr.setSz(sz);
        rPr.setSzCs(sz);
        Color color = new Color();
        color.setVal(docLvl.getDocLvlFormat().getColor()); // 设置圆点颜色为红色，RGB值为FF0000
        rPr.setColor(color);
        lvl.setRPr(rPr);
        //将Lvl增加到AbstractNum中
        an.getLvl().add(lvl);


        //将AbstractNum增加到Numbering中
        numbering.getAbstractNum().add(an);

        //设置一个Num的实例，通过AbstractNumId引用刚定义的AbstractNum
        Numbering.Num.AbstractNumId anid = new Numbering.Num.AbstractNumId();
        anid.setVal(abstractNumId);
        Numbering.Num num = new Numbering.Num();
        num.setAbstractNumId(anid);
        num.setNumId(abstractNumId); //此处NumId不能为0，必须为正整数
        //将Num增加到Numbering中
        numbering.getNum().add(num);
    }
}
